import time
import logging
import random
from typing import Callable, Any, Optional
from dataclasses import dataclass
import threading
from ..utils.config_loader import ConfigLoader

class SmartWait:
    """
    智能等待机制，用于优化测试过程中的等待逻辑
    - 支持条件等待
    - 自适应超时时间
    - 指数退避重试
    - 等待状态监控
    """
    
    def __init__(self, config_path: str = None):
        """
        初始化智能等待
        
        Args:
            config_path: 配置文件路径
        """
        # 加载配置
        self.config_loader = ConfigLoader(config_path)
        self.config = self.config_loader.get_config()['smart_wait']
        
        # 默认配置
        self.default_timeout = self.config['default_timeout']
        self.polling_interval = self.config['polling_interval']
        self.adaptive_timeout = self.config['adaptive_timeout']
        self.max_retries = self.config['max_retries']
        self.backoff_factor = self.config['backoff_factor']
        
        # 统计信息
        self.wait_stats = {
            'total_waits': 0,
            'successful_waits': 0,
            'failed_waits': 0,
            'total_wait_time': 0,
            'average_wait_time': 0
        }
        
        # 等待历史，用于自适应超时计算
        self.wait_history = []
        
        # 初始化日志
        logging.basicConfig(
            level=getattr(logging, self.config_loader.get_config()['logging']['level']),
            format=self.config_loader.get_config()['logging']['format']
        )
        self.logger = logging.getLogger('SmartWait')
        
        self.logger.info("智能等待机制初始化完成")
    
    def until(self, condition: Callable[[], bool], timeout: Optional[float] = None, 
              message: str = "条件未满足", polling_interval: Optional[float] = None) -> bool:
        """
        等待直到条件满足或超时
        
        Args:
            condition: 条件函数，返回True表示条件满足
            timeout: 超时时间（秒），None表示使用默认值或自适应值
            message: 超时错误消息
            polling_interval: 轮询间隔（秒），None表示使用默认值
            
        Returns:
            是否成功等待到条件满足
            
        Raises:
            TimeoutError: 当超时发生时
        """
        start_time = time.time()
        self.wait_stats['total_waits'] += 1
        
        # 确定超时时间
        if timeout is None:
            if self.adaptive_timeout:
                timeout = self._calculate_adaptive_timeout()
            else:
                timeout = self.default_timeout
        
        # 确定轮询间隔
        if polling_interval is None:
            polling_interval = self.polling_interval
        
        # 开始等待
        self.logger.debug(f"开始等待条件满足，超时时间: {timeout}秒")
        
        while time.time() - start_time < timeout:
            # 检查条件
            try:
                if condition():
                    # 条件满足，记录等待时间
                    wait_time = time.time() - start_time
                    self._update_wait_stats(True, wait_time)
                    self.logger.debug(f"条件满足，等待时间: {wait_time:.3f}秒")
                    return True
            except Exception as e:
                self.logger.warning(f"条件检查异常: {str(e)}")
            
            # 等待一段时间后重试
            time.sleep(polling_interval)
        
        # 超时
        wait_time = time.time() - start_time
        self._update_wait_stats(False, wait_time)
        self.logger.error(f"等待超时: {message}，已等待 {wait_time:.3f}秒")
        raise TimeoutError(f"{message} (超时时间: {timeout}秒)")
    
    def wait_for_element(self, element_locator: Callable, timeout: Optional[float] = None, 
                        message: str = "元素未找到") -> Any:
        """
        等待元素出现并返回
        
        Args:
            element_locator: 元素定位函数，成功返回元素，失败抛出异常或返回None
            timeout: 超时时间
            message: 错误消息
            
        Returns:
            找到的元素
        """
        element = None
        
        def condition():
            nonlocal element
            try:
                element = element_locator()
                return element is not None
            except Exception:
                return False
        
        self.until(condition, timeout, message)
        return element
    
    def wait_for_page_load(self, is_loaded_checker: Callable[[], bool], timeout: Optional[float] = None) -> bool:
        """
        等待页面加载完成
        
        Args:
            is_loaded_checker: 页面加载状态检查函数
            timeout: 超时时间
            
        Returns:
            是否加载完成
        """
        return self.until(is_loaded_checker, timeout, "页面加载超时")
    
    def retry(self, func: Callable, max_retries: Optional[int] = None, 
              backoff_factor: Optional[float] = None, exceptions: tuple = Exception) -> Any:
        """
        重试执行函数直到成功或达到最大重试次数
        
        Args:
            func: 要执行的函数
            max_retries: 最大重试次数
            backoff_factor: 退避因子
            exceptions: 捕获的异常类型
            
        Returns:
            函数执行结果
        """
        if max_retries is None:
            max_retries = self.max_retries
        if backoff_factor is None:
            backoff_factor = self.backoff_factor
        
        last_exception = None
        
        for attempt in range(max_retries + 1):  # +1 因为包含第一次尝试
            try:
                result = func()
                self.logger.debug(f"函数执行成功，尝试次数: {attempt + 1}")
                return result
            except exceptions as e:
                last_exception = e
                if attempt < max_retries:
                    # 计算退避时间
                    wait_time = (backoff_factor ** attempt) * (0.5 + random.random())
                    self.logger.debug(f"尝试 {attempt + 1} 失败，{wait_time:.3f}秒后重试: {str(e)}")
                    time.sleep(wait_time)
                else:
                    self.logger.error(f"达到最大重试次数 {max_retries}，执行失败")
        
        # 所有尝试都失败
        raise last_exception
    
    def _calculate_adaptive_timeout(self) -> float:
        """
        基于历史等待时间计算自适应超时
        
        Returns:
            计算出的超时时间
        """
        if not self.wait_history or len(self.wait_history) < 5:
            return self.default_timeout
        
        # 使用90%分位数作为基础，加上安全边际
        sorted_history = sorted(self.wait_history)
        percentile_90 = sorted_history[int(len(sorted_history) * 0.9)]
        adaptive_timeout = percentile_90 * 1.5  # 50%的安全边际
        
        # 确保超时时间在合理范围内
        adaptive_timeout = max(5.0, min(adaptive_timeout, self.default_timeout * 2))
        
        self.logger.debug(f"计算自适应超时时间: {adaptive_timeout:.3f}秒")
        return adaptive_timeout
    
    def _update_wait_stats(self, success: bool, wait_time: float):
        """
        更新等待统计信息
        
        Args:
            success: 是否成功
            wait_time: 等待时间
        """
        if success:
            self.wait_stats['successful_waits'] += 1
            # 只记录成功的等待时间用于历史分析
            self.wait_history.append(wait_time)
            # 保持历史记录在合理范围内
            if len(self.wait_history) > 100:
                self.wait_history = self.wait_history[-100:]
        else:
            self.wait_stats['failed_waits'] += 1
        
        self.wait_stats['total_wait_time'] += wait_time
        total_waits = self.wait_stats['total_waits']
        if total_waits > 0:
            self.wait_stats['average_wait_time'] = self.wait_stats['total_wait_time'] / total_waits
    
    def get_stats(self) -> dict:
        """
        获取等待统计信息
        
        Returns:
            统计信息字典
        """
        return self.wait_stats.copy()
    
    def reset_stats(self):
        """
        重置等待统计信息
        """
        self.wait_stats = {
            'total_waits': 0,
            'successful_waits': 0,
            'failed_waits': 0,
            'total_wait_time': 0,
            'average_wait_time': 0
        }
        self.wait_history = []
        self.logger.info("等待统计信息已重置")

# 全局智能等待实例，方便使用
_smart_wait_instance = None
_instance_lock = threading.Lock()

def get_smart_wait(config_path: str = None) -> SmartWait:
    """
    获取全局智能等待实例
    
    Args:
        config_path: 配置文件路径
        
    Returns:
        SmartWait实例
    """
    global _smart_wait_instance
    with _instance_lock:
        if _smart_wait_instance is None:
            _smart_wait_instance = SmartWait(config_path)
    return _smart_wait_instance

# 便捷函数
def wait_until(condition: Callable[[], bool], timeout: Optional[float] = None, 
               message: str = "条件未满足") -> bool:
    """便捷函数：等待直到条件满足"""
    return get_smart_wait().until(condition, timeout, message)

def wait_for_element(element_locator: Callable, timeout: Optional[float] = None) -> Any:
    """便捷函数：等待元素出现"""
    return get_smart_wait().wait_for_element(element_locator, timeout)

def retry(func: Callable, max_retries: Optional[int] = None) -> Any:
    """便捷函数：重试执行函数"""
    return get_smart_wait().retry(func, max_retries)